package logger

import (
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	"runtime"
	"sync"
	"time"
)

type Setting struct {
	Path       string `yaml:"path"`
	Name       string `yaml:"name"`
	Ext        string `yaml:"ext"`
	TimeFormat string `yaml:"time-format"`
}

var (
	logFilePtr         *os.File
	defaultPrefix      = ""
	defaultCallerDepth = 2
	logPtr             *log.Logger
	mu                 sync.Mutex
	logPrefix          = ""
	levelFlagSlc       = []string{"DEBUG", "INFO", "WARN", "ERROR", "FATAL"}
)

type logLevel int

const (
	debugLevel logLevel = iota
	infoLevel
	warningLevel
	errorLevel
	fatalLevel
)

const flags = log.LstdFlags

func init() {
	logPtr = log.New(os.Stdout, defaultPrefix, flags)
}

func Config(settingPtr *Setting) {
	var err error
	dir := settingPtr.Path
	fileName := fmt.Sprintf("%s-%s.%s",
		settingPtr.Name,
		time.Now().Format(settingPtr.TimeFormat),
		settingPtr.Ext)
	filePtr, err := mustOpen(fileName, dir)
	if err != nil {
		log.Fatalf("logging.Setup err: %s", err)
	}
	mw := io.MultiWriter(os.Stdout, filePtr)
	logPtr = log.New(mw, defaultPrefix, flags)
}

func Info(v ...interface{}) {
	mu.Lock()
	defer mu.Unlock()
	setPrefix(infoLevel)
	logPtr.Println(v...)
}

func Debug(v ...interface{}) {
	mu.Lock()
	defer mu.Unlock()
	setPrefix(debugLevel)
	logPtr.Println(v...)
}

func Warn(v ...interface{}) {
	mu.Lock()
	defer mu.Unlock()
	setPrefix(warningLevel)
	logPtr.Println(v...)
}

func Error(v ...interface{}) {
	mu.Lock()
	defer mu.Unlock()
	setPrefix(errorLevel)
	logPtr.Println(v...)
}

func Fatal(v ...interface{}) {
	mu.Lock()
	defer mu.Unlock()
	setPrefix(fatalLevel)
	logPtr.Fatalln(v...)
}

func setPrefix(level logLevel) {
	_, file, line, ok := runtime.Caller(defaultCallerDepth)
	if ok {
		logPrefix = fmt.Sprintf("[%s][%s:%d] ", levelFlagSlc[level], filepath.Base(file), line)
	} else {
		logPrefix = fmt.Sprintf("[%s] ", levelFlagSlc[level])
	}
	logPtr.SetPrefix(logPrefix)
}

func mustOpen(fileName, dir string) (*os.File, error) {
	_, err := os.Stat(dir)
	if os.IsPermission(err) {
		return nil, fmt.Errorf("permission denied dir: %s", dir)
	}

	if os.IsNotExist(err) {
		err := os.MkdirAll(dir, os.ModePerm)
		if err != nil {
			return nil, fmt.Errorf("error during make dir %s, err: %s", dir, err)
		}
	}

	f, err := os.OpenFile(dir+string(os.PathSeparator)+fileName, os.O_APPEND|os.O_CREATE|os.O_RDWR, 0644)
	if err != nil {
		return nil, fmt.Errorf("fail to open file, err: %s", err)
	}

	return f, nil
}
